

#include <QFileInfo>
#include <QLibrary>
#include <QDir>
#include "rsgispluginregistry.h"
#include "../RSGISPlugin/rsgisplugin.h"
#include "rsgiswindow.h"

typedef RSGISPlugin *create_ui( RSGISInterface * qI );
typedef QString name_t();
typedef QString description_t();
typedef QString category_t();
typedef int type_t();

RSGISPluginRegistry* RSGISPluginRegistry::m_sInstance = nullptr;
RSGISPluginRegistry *RSGISPluginRegistry::instance()
{
    if ( nullptr == m_sInstance )
    {
        m_sInstance = new RSGISPluginRegistry();
    }

    return m_sInstance;
}

void RSGISPluginRegistry::setInterface(RSGISInterface *iface)
{
    m_pInterface = iface;
}

bool RSGISPluginRegistry::isLoaded(QString key) const
{
    QMap<QString, RSGISPluginMetadata>::const_iterator it = m_Plugins.find( key );
    if ( it != m_Plugins.end() ) // 搜寻C++插件
        return true;

    return false;
}

QString RSGISPluginRegistry::library(QString key)
{
    QMap<QString, RSGISPluginMetadata>::iterator it = m_Plugins.find( key );
    if ( it != m_Plugins.end() )
        return it->library();

    return QString();
}

RSGISPlugin *RSGISPluginRegistry::plugin(QString key)
{
    QMap<QString, RSGISPluginMetadata>::iterator it = m_Plugins.find( key );
    if ( it == m_Plugins.end() )
        return nullptr;

    return it->plugin();
}

void RSGISPluginRegistry::addPlugin(QString key, RSGISPluginMetadata metadata)
{
    m_Plugins.insert( key, metadata );
}

void RSGISPluginRegistry::removePlugin(QString key)
{
    QMap<QString, RSGISPluginMetadata>::iterator it = m_Plugins.find( key );
    if ( it != m_Plugins.end() )
    {
        m_Plugins.erase( it );
    }
}

void RSGISPluginRegistry::unloadAll()
{
    for ( QMap<QString, RSGISPluginMetadata>::iterator it = m_Plugins.begin();
          it != m_Plugins.end();
          ++it )
    {
        if ( it->plugin() )
        {
            it->plugin()->unload();
        }
        else
        {
        }
    }
}

//当前没有实现该序列化功能，后续实现
void RSGISPluginRegistry::dump()
{

}

void RSGISPluginRegistry::loadCppPlugin(QString theFullPathName)
{
//    QSettings settings;

    QString baseName = QFileInfo( theFullPathName ).baseName();

    // first check to see if its already loaded
    if ( isLoaded( baseName ) )
    {
        // plugin is loaded
        return;
    }

    QLibrary myLib( theFullPathName );

    QString myError; //we will only show detailed diagnostics if something went wrong
    myError += QObject::tr( "Library name is %1\n" ).arg( myLib.fileName() );

    bool loaded = myLib.load();
    if ( !loaded )
    {
//        QgsMessageLog::logMessage( QObject::tr( "Failed to load %1 (Reason: %2)" ).arg( myLib.fileName() ).arg( myLib.errorString() ), QObject::tr( "Plugins" ) );
        return;
    }

    myError += QObject::tr( "Attempting to resolve the classFactory function\n" );

    type_t *pType = ( type_t * ) ( myLib.resolve( "type" ) );
    name_t *pName = ( name_t * ) ( myLib.resolve( "name" ) );

    switch ( pType() )
    {
    case RSGISPlugin::RENDERER:
    case RSGISPlugin::UI:
    {
        // UI only -- doesn't use mapcanvas
        create_ui *cf = ( create_ui * ) ( myLib.resolve( "classFactory" ) );
        if ( cf )
        {
            RSGISPlugin *pl = cf( m_pInterface );
            if ( pl )
            {
                pl->initGui();
                // add it to the plugin registry
                addPlugin( baseName, RSGISPluginMetadata( myLib.fileName(), pName(), pl ) );
                //add it to the qsettings file [ts]
//                settings.setValue( "/Plugins/" + baseName, true );
//                QgsMessageLog::logMessage( QObject::tr( "Loaded %1 (Path: %2)" ).arg( pName() ).arg( myLib.fileName() ), QObject::tr( "Plugins" ), QgsMessageLog::INFO );

                QObject *o = dynamic_cast<QObject *>( pl );
                if ( o )
                {
//                    QgsDebugMsg( QString( "plugin object name: %1" ).arg( o->objectName() ) );
                    if ( o->objectName().isEmpty() )
                    {
#ifndef WIN32
                        baseName = baseName.mid( 3 );
#endif
//                        QgsDebugMsg( QString( "object name to %1" ).arg( baseName ) );
                        o->setObjectName( QString( "qgis_plugin_%1" ).arg( baseName ) );
//                        QgsDebugMsg( QString( "plugin object name now: %1" ).arg( o->objectName() ) );
                    }

                    if ( !o->parent() )
                    {
//                        QgsDebugMsg( QString( "setting plugin parent" ) );
                        o->setParent( RSGISWindow::instance() );
                    }
                    else
                    {
//                        QgsDebugMsg( QString( "plugin parent already set" ) );
                    }
                }
            }
            else
            {
                // something went wrong
//                QMessageBox::warning( m_pInterface->mainWindow(), QObject::tr( "Error Loading Plugin" ),
//                                      QObject::tr( "There was an error loading a plugin."
//                                                   "The following diagnostic information may help the QGIS developers resolve the issue:\n%1." )
//                                      .arg( myError ) );
                //disable it to the qsettings file [ts]
//                settings.setValue( "/Plugins/" + baseName, false );
            }
        }
        else
        {
//            QgsMessageLog::logMessage( QObject::tr( "Unable to find the class factory for %1." ).arg( theFullPathName ), QObject::tr( "Plugins" ) );
        }

    }
        break;
    default:
        // type is unknown
//        QgsMessageLog::logMessage( QObject::tr( "Plugin %1 did not return a valid type and cannot be loaded" ).arg( theFullPathName ), QObject::tr( "Plugins" ) );
        break;
    }
}

void RSGISPluginRegistry::unloadCppPlugin(QString theFullPathName)
{
    QString baseName = QFileInfo( theFullPathName ).baseName();
    if ( isLoaded( baseName ) )
    {
        RSGISPlugin * pluginInstance = plugin( baseName );
        if ( pluginInstance )
        {
            pluginInstance->unload();
        }
        // remove the plugin from the registry
        removePlugin( baseName );
//        QgsDebugMsg( "Cpp plugin successfully unloaded: " + baseName );
    }
}

void RSGISPluginRegistry::restoreSessionPlugins(QStringList thePluginDirList)
{
    QStringListIterator myIterator( thePluginDirList );
    while ( myIterator.hasNext() )
    {
        restoreSessionPlugins( myIterator.next() );
    }
}

void RSGISPluginRegistry::restoreSessionPlugins(QString thePluginDirString)
{

#if defined(WIN32) || defined(__CYGWIN__)
    QString pluginExt = "*.dll";
#elif ANDROID
    QString pluginExt = "*plugin.so";
#else
    QString pluginExt = "*.so*";
#endif

    // check all libs in the current plugin directory and get name and descriptions
    QDir myPluginDir( thePluginDirString, pluginExt, QDir::Name | QDir::IgnoreCase, QDir::Files | QDir::NoSymLinks );

    for ( uint i = 0; i < myPluginDir.count(); i++ )
    {
        QString myFullPath = thePluginDirString + "/" + myPluginDir[i];
        if ( checkCppPlugin( myFullPath ) )
        {
            // check if the plugin was active on last session

            QString baseName = QFileInfo( myFullPath ).baseName();
//            if ( mySettings.value( "/Plugins/" + baseName ).toBool() )
            {
                loadCppPlugin( myFullPath );
            }
        }
    }
}

QList<RSGISPluginMetadata *> RSGISPluginRegistry::pluginData()
{
    QList<RSGISPluginMetadata*> resultList;
    QMap<QString, RSGISPluginMetadata>::iterator it = m_Plugins.begin();
    for ( ; it != m_Plugins.end(); ++it )
    {
        resultList.push_back( &( it.value() ) );
    }
    return resultList;
}

RSGISPluginRegistry::RSGISPluginRegistry()
{
    m_Plugins.clear();
    m_pInterface = nullptr;
}

bool RSGISPluginRegistry::checkCppPlugin(QString pluginFullPath)
{
    QLibrary myLib( pluginFullPath );
    bool loaded = myLib.load();
    if ( ! loaded )
    {
//        QgsMessageLog::logMessage( QObject::tr( "Failed to load %1 (Reason: %2)" ).arg( myLib.fileName() ).arg( myLib.errorString() ), QObject::tr( "Plugins" ) );
        return false;
    }

    name_t * myName = ( name_t * ) ( myLib.resolve( "name" ) );
    description_t *  myDescription = ( description_t * )  ( myLib.resolve( "description" ) );
    category_t *  myCategory = ( category_t * )  ( myLib.resolve( "category" ) );
    version_t *  myVersion = ( version_t * ) ( myLib.resolve( "version" ) );

    if ( myName && myDescription && myVersion  && myCategory )
        return true;

//    QgsDebugMsg( "Failed to get name, description, category or type for " + myLib.fileName() );
    return false;
}

